/// <refernece types="node" />
/// <reference types="mz" />

import fs = require('fs');
import mzfs = require('mz/fs');
import path = require('path');
import config = require('./config');
import { Socket } from 'libtcp';

export type BeforeReceiveFunction = (fileinfo: config.Fileinfo, message: any) => Promise<boolean>; // return true if accept
export type ReceivingFunction = (fileinfo: config.Fileinfo, message: any, received_size: number) => any;
export type ReceivedFunction = (fileinfo: config.Fileinfo, message: any, content: Buffer) => any;

export async function sendFile(filename: string, message: any, socket: Socket) {
	let stat = await mzfs.stat(filename);
	if (!stat.isFile()) {
		throw new Error('only file can be sent');
	}
	let fileinfo: config.Fileinfo = {
		filename: path.basename(filename),
		total_size: stat.size
	};
	let index: number = <number>await socket.emitSync(config.FILE_MESSAGE, { fileinfo: fileinfo, message: message });
	let CONTENT_MESSAGE = config.FILE_MESSAGE + index + "content";
	let fd = await mzfs.open(filename, "r");
	let sent_size = 0;
	while (sent_size < fileinfo.total_size) {
		let ret = await mzfs.read(fd, new Buffer(config.BUFFER_SIZE), 0, config.BUFFER_SIZE, sent_size);
		socket.emit(CONTENT_MESSAGE, ret[1].slice(0, ret[0]));
		sent_size += ret[0];
	}
}

export async function receiveFile(index: number, fileinfo: config.Fileinfo, message: any, socket: Socket,
	before_receive: BeforeReceiveFunction, receiving: ReceivingFunction, received: ReceivedFunction) {

	if (before_receive == null) {
		throw new Error('before_receive function not set');
	}
	let accept = await before_receive(fileinfo, message);
	if (accept) {
		let CONTENT_MESSAGE = config.FILE_MESSAGE + index + "content";
		let contents: Buffer[] = [];
		let received_size: number = 0;

		let content_listener = (content: Buffer) => {
			contents.push(content);
			received_size += content.length;
			if (receiving) receiving(fileinfo, message, received_size);
			if (received_size >= fileinfo.total_size) {
				socket.removeListener(CONTENT_MESSAGE, content_listener);
				if (received) received(fileinfo, message, Buffer.concat(contents));
			}
		}
		socket.on(CONTENT_MESSAGE, content_listener);
		return index;
	} else {
		throw new Error('not accepted');
	}
}